﻿using System.Collections.Generic;
using FarseerGames.FarseerPhysics.Mathematics;
using FreeGoo.GameEngine.Behaviors;
using FreeGoo.GameEngine.Renderables;
using FreeGoo.GameEngine.Renderables.Particles;

namespace FreeGoo.GameEngine
{
    public class Goal : Renderable
    {
        protected const float DefaultVacuumRange = ParticleFactory.DefaultSimpleParticleRadius * 4;
        protected const float DefaultVacuumStrength = 200;
        protected const float DefaultRescueRange = 4f;
        private List<string> _acceptsParticleTypes;

        public Goal(Vector2 position, string acceptsParticleTypes)
            : base()
        {
            Position = position;
            SetAcceptsParticleTypes(acceptsParticleTypes);
            VacuumRange = DefaultVacuumRange;
            RescueRange = DefaultRescueRange;
            VacuumStrength = DefaultVacuumStrength;

            AddBehavior(new Rotator(this) { Speed = 0.75f });
        }

        public override Vector2 Position { get; set; }
        public override float Rotation { get; set; }
        public bool AcceptsAll { get; set; }
        public float VacuumRange { get; set; }
        public float RescueRange { get; set; }
        public int RescuedCount { get; set; }
        public float VacuumStrength { get; set; }

        public override void AddToEngine(Engine engine)
        {
            base.AddToEngine(engine);
            Engine.Goals.Add(this);
        }

        public override void Delete()
        {
            base.Delete();
            Engine.Goals.Remove(this);
        }

        public bool Accepts(Particle particle)
        {
            return
                !particle.IsLinked &&
                (AcceptsAll || _acceptsParticleTypes.Contains(particle.GetType().Name));
        }

        public override void UpdatePhysics()
        {
            // Consume up any goos that are close enough
            // Add a sucking force to all goos that are close enough, if there are any goos that are close
            // enough
            base.UpdatePhysics();

            RescueParticles();
            VacuumParticles();
        }

        public override void UpdateGraphics()
        {
            base.UpdateGraphics();
        }

        private void RescueParticles()
        {
            IList<Particle> particlesToRescue = Engine.GetParticlesNearPosition(Position, RescueRange);
            foreach (Particle particle in particlesToRescue)
            {
                if (Accepts(particle))
                {
                    particle.ScheduledForDelete = true;
                    RegisterRescuedParticle(particle);
                }
            }
        }

        private void VacuumParticles()
        {
            IList<Particle> particlesToVacuum = Engine.GetParticlesNearPosition(Position, VacuumRange);
            foreach (Particle particle in particlesToVacuum)
            {
                if (PullsOn(particle))
                {
                    Vector2 difference = Position - particle.Body.Position;
                    float distance = difference.Length();
                    float strength = 1 - distance / VacuumRange;

                    Vector2 force = VacuumStrength * (strength * difference * particle.Body.Mass);
                    particle.Body.ApplyForce(force);
                }
            }
        }

        private bool PullsOn(Particle particle)
        {
            return particle.IsLinked;
        }

        private void RegisterRescuedParticle(Particle particle)
        {
            // TODO: Keep track of the number of different particles consumed
            RescuedCount++;
        }

        private void SetAcceptsParticleTypes(string acceptsParticleTypes)
        {
            if (acceptsParticleTypes.ToLowerInvariant().Equals("all"))
            {
                AcceptsAll = true;
            }
            else
            {
                string[] accepts = acceptsParticleTypes.Split(',');
                _acceptsParticleTypes = new List<string>();
                _acceptsParticleTypes.AddRange(accepts);
            }
        }
    }
}
